package com.showapi.app.dynamic

import com.alibaba.fastjson.JSON
import com.showapi.app.util.CommonUtil
import com.showapi.app.util.ConstantsOfShowapi
import groovy.io.FileType
import org.apache.commons.lang3.StringUtils
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.context.ApplicationContext
import org.springframework.core.io.ClassPathResource

public abstract class BaseLine   {
	static Logger log= LoggerFactory.getLogger(BaseNode.class)

	public abstract String  doGetPackageName() 	//包名
	public  String dirPath="springboot${File.separator}node_of_flow".toString()
	public  String node_path
	def allClassCacheMap=[:]//存放所有类 ,key是文件的path,value是[md5:"",clazz:aClass]
	def nodeClassCacheMap=[:]//只存放handler类,key是文件的path,value是[md5:"",clazz:aClass]
	def nodeInstanceCacheMap=[:]	//下属节点实例缓存
	def ShowapiClassLoader showapiClassLoader

	def getRetObj= CommonUtil.&getRetObj
	def removeProperty= CommonUtil.&removeProperty

	ApplicationContext ctx

	//验证。返回非null时，流程终止。并把其object返回
	public  Object	doVerification(Map reqParams) {
		return null
	}
	//流程前置处理
	public  void	doBeforeLine(Map reqParams) {
	}

	//流程后置处理
	public  Map	doAfterLine( Map lastNodeResult, Map reqParams,Map lineParams) {
		return 	lastNodeResult
	}

	//验证流程中止条件
	public  boolean	doStopLineCondition(Map ret,Map reqParams,Map lineParams) {
		return false
	}


	public Map run(Map reqParams){
		def ret=doVerification(reqParams)
		if(ret)return ret
		doBeforeLine(reqParams)  //调用前置
		def lineParams=newLineParams(reqParams)
		def newlist=getHandlerList(reqParams,lineParams)
		newlist.sort{a,b->
			return a.doSortWeight(reqParams,lineParams)-b.doSortWeight(reqParams,lineParams)
		}
		newlist.find{node->
			if(lineParams.onlyRunClass){
				def name=node.getClass().getName()
				int ind=name.lastIndexOf(".")
				name=name.substring(ind+1)
				if( name!=lineParams.onlyRunClass ){return}   //不符合名称，返回

			}

			try{
//				log.info "run in "+node
				ret=node.wrapAction(reqParams,lineParams)
//				log.info "after wrapAction  ,ret is: "+ret
				if(lineParams.stopLine)return 1
				else{//如果控制参数中不终止，则调用do_stopCheck进行判断
					def stop=doStopLineCondition(ret,reqParams,lineParams)
					if(stop)return 1
				}

			}catch(Exception e){
				e.printStackTrace()
			}
		}
		ret= doAfterLine(ret, reqParams,lineParams)  //调用后置,最后一次更改返回结果的机会
//		log.info "run_flow result is:"+ret.toString()
		return ret;
	}


	//生成控制参数
	public Map newLineParams(reqParams){
		def lineParams=_getLineParamsModel()
		def onlyRunClass=reqParams?.onlyRunClass
		if(onlyRunClass){
			reqParams.remove("onlyRunClass")
			lineParams.onlyRunClass=onlyRunClass
		}
		return lineParams
	}



	//返回nodeList
	//在外围要控制同步
	public List   getHandlerList(reqParams,lineParams){
		def ret
		def newlist=[]
		nodeInstanceCacheMap.each{className,nodeObj->
			if(nodeObj?.doIsMatch(reqParams,lineParams)){
				newlist<<nodeObj
			}
		}
		return newlist
	}


	//填充allClassCacheMap
	public void  reloadClass(){
		init()
		checkParentModify()  //检查父级或工具类改变情况。如果改变，则全体重新编译
		def pack_path=doGetPackageName().replaceAll("\\.","/")
		if(!pack_path)return
		File nodeFileDir=new File(node_path+pack_path)
		println nodeFileDir

		if (nodeFileDir.exists()){
			nodeFileDir.eachFileRecurse(FileType.FILES){ File file->
				def path=file.getAbsolutePath()
				if(!path.endsWith(".groovy"))return
				int ind=path.indexOf(dirPath)
				int end=path.lastIndexOf(".groovy")
				if(end==-1)return
				def className=path.substring(ind+dirPath.size()+1,end)  //+1是把斜线也算进去了
				className=StringUtils.replace(className, "\\",".")
				className=StringUtils.replace(className, "/",".")
				showapiClassLoader.loadClass(className)  //里面会递归加载import，extends及自身三样
			}
		}

	}



	public BaseNode  newNode(Class aClass ){
		def node=aClass.newInstance();
		node.ctx=ConstantsOfShowapi.ctx
		node.line=this
		node.doInitNode( )
		return node

	}

	public init(){
		if(showapiClassLoader)return
		def IS_DEV= ConstantsOfShowapi.IS_DEV
        ClassLoader parent =BaseLine.class.getClassLoader();
		def filepath
		if(IS_DEV){//需要找到classpath
			filepath=new File("").getAbsolutePath()
			node_path="${filepath}/src/main/resources/static/${dirPath}/".toString()
			log.info "in   DEVELOPMENT env,node_path is :"+node_path
			log.info "filepath is :"+filepath
			log.info "dirPath :"+dirPath
		}else{
			ClassPathResource resource = new ClassPathResource("application.properties" );
			filepath=new File(resource.getFile().getParentFile(),"static").getAbsolutePath() //向上一级
			node_path="${filepath}/${dirPath}/".toString()
			log.info "in   PRODUCTION env,node_path is :"+node_path
		}

		showapiClassLoader=new ShowapiClassLoader(parent);
	}

	//检查父级或工具类改变情况。如果改变，则全体重新编译
	public void checkParentModify(){
		boolean needAllReload=false
		allClassCacheMap.find{className,item->
			if(item.isParentOrUtil){//只检查父级或工具类
				def f=new File(item.filePath)
				def src=f.getText("utf-8")
				def now_md5=src.md5()
				if(item.md5!=now_md5){
					needAllReload=true //标记为需要全部重新加载
					return 1
				}
			}
		}
		log.info "needAllReload  is(step 2 of flow ): "+needAllReload
		if(needAllReload){
			allClassCacheMap.clear()
		}

	}

	//加载父类
	public   loadParentClass={src->
		int ind=src.indexOf("{")  //读到这个标记认为引用头结束
		src=src.substring(0,ind)
		def  pack_name
		src.eachLine{
			def line=it.trim()
			if(!line)return
			if(line.startsWith("//"))return
			if(line.contains("*"))return
			if(!pack_name){
				line.find(/(?is)\s?package\s+([\w\.]+)/){mat,pa->
					pack_name=pa
				}
				if(pack_name)return  //找到就返回
			}

			boolean is_import=false
			line.find(/(?is).*?import\s+([\w\.]+)/){mat,className->
//				log.info "loadCommonClass    "+className
				is_import=true
				showapiClassLoader.loadClass(className)  //加载import
			}
			if(!is_import){ //加载extend
				line.find(/(?is)\s?class\s+[\w]+\s+extends\s+([\w\.]+)/){mat,className->
					if(!className.contains(".")){//不是全名
						className=pack_name+"."+className
					}
					showapiClassLoader.loadClass(className)
					def item=nodeClassCacheMap.get(className)
					if(item){
						nodeClassCacheMap.remove(className) //extend的类不出现在此列表里
						item.isParentOrUtil=true  //是父级或工具类
					}

				}
			}
		}
	}

	public  class  ShowapiClassLoader extends  ClassLoader{
		public ShowapiClassLoader(parentLoader){
			super(parentLoader)
		}
		protected Class<?> findClass(String className)
				throws ClassNotFoundException {

			def lineObj=BaseLine.this
			def pack_path=className.replaceAll("\\.","/")
			def filePath=lineObj.node_path+pack_path+".groovy"
			def f=new File(filePath)
			if(!f.exists()){return}

			def item=lineObj.allClassCacheMap.get(className)
			def src
			def modify_time=f.lastModified()

			if(!item){
				src=f.getText("utf-8")
				lineObj.loadParentClass(src)  //先加载父类
			}else {
				if(item.modify_time==modify_time){	//时间没改过，直接返回
					println "same modify_time ,find in cache: "+item.clazz
					return item.clazz
				}
				//运行到这里，说明文件肯定改过
				src=f.getText("utf-8")
			}
			def now_md5=src.md5()

            Class clazz
			if(item?.md5==now_md5) {//时间改变过，但内容没变
				item.modify_time=modify_time
				return item.clazz
			}else{
				try{
					GroovyClassLoader onceClassLoader=new GroovyClassLoader(this )
					println("parse file "+className)
					clazz=onceClassLoader.parseClass(f)
					item=[md5:now_md5,clazz:clazz,filePath:filePath,modify_time:modify_time]
					if(BaseNode.class.isAssignableFrom(clazz)){ //放入node缓存
						lineObj.nodeClassCacheMap.put(className,item)
						lineObj.nodeInstanceCacheMap.put(className,lineObj.newNode(clazz))
					}else{
						item.isParentOrUtil=true  //是父级或工具类
					}
					lineObj.allClassCacheMap.put(className,item)

				}catch(Exception e){
					e.printStackTrace()
					log.error("reloadClass  err")
				}
			}
			return clazz
		}
		
	}
	def propertyMissing(pName){
//		log.info "find property:  "+pName
		return ctx.getBean(pName)
	}
	def methodMissing(String name, def args) {
		throw new Exception("cant find method "+name)
	}




	//取得【流程控制参数】数据模型
	public Map  _getLineParamsModel(reqParams){
		def lineParams=[:]
		lineParams.shareMap=[:]//多个node间共享的map
		lineParams.onlyRunClass=""// 可能只想跑某个node，则在此写Handler的全名。比如Local_news_aomeng。空或不写表示所有都要跑
		lineParams.stopLine=false	//停止整个流程
		lineParams.needSave=false	//是否需要最终保存


		return lineParams

	}

}
